Viet TP's notepage
Welcome!! Viet is a Microsoft Student Partner in Vietnam
Enjoy WPF: make TabControl in WPF look like Browser Tabs
This article discusses: | This article uses the following technologies: |
|
WPF, XAML, C# |
##Source code:
http://cid-82ca521511c25e79.skydrive.live.com/self.aspx/Blog%20files/SourceCode/WebTabs.rar
Nowaday, Web Browsers mostly use tabs to browse many pages in a single window. Last night, while I was developing a small application, I found a funny trick with TabControl. My app has many forms arranged in many TabItem objects. I want the TabControl load only some particular TabItem objects and hide the others. It’s necessary to use databinding. In this case, controls (TabItem objects) are also data, and I bind a control (TabControl.ItemsSource) to control-data collection. What a thought! :D
Using TabControl
TabControl, an UIControl, is a Control in which we add TabItem objects. We can declare it in XAML:
As other controls that contain many child-controls, TabControl has a Property named Items – a collection of object. TabItem objects declared in XAML actually are added to this collection. And besides declaring the collection in design-time, we can use databinding with it in code-behind.
Making CustomControl with UserControl template
With UserControl template in Visual Studio, we have a tricky way to make a TabItem control with edited visual.
Add new UserControl to project: Right-click on the project name in Solution Explorer –> Add –> User Control.
In the dialog, enter Name: WebTab.xaml. VS create 2 new files (WebTab.xaml and WebTab.xaml.cs).
In file WebTab.xaml, UserControl’s root element is UserControl. The trick is that we can change it into TabItem and now we have a custom TabItem control.
I define a custom TabItem that the header has a TextBlock represent the title, and a button to close this tab in main window; the content of TabItem is a Frame control – a control contains a WebBrowser object represent web page.
<TabItem.Header><DockPanel><TextBlock x:Name="txtHeader" Text="{Binding Header}" DockPanel.Dock="Left" Margin="2"/><Button Style="{StaticResource ExitTabButtonStyle}" Click="Button_Click"/></DockPanel></TabItem.Header><TabItem.Content><Frame x:Name="myFrame"/></TabItem.Content>ExitTabButtonStyle is a simple Style I define in the <TabItem.Resources> collection and you can find it in source code attached.
In file WebTab.xaml.cs, I define a field named myParent to keep the collection contains this tab. And I create an event handler Button_Click to remove this tab when button is clicked.
DataBinding with Collection
Class used for data source of binding to collection is System.Collections.ObjectModel.ObservableCollection<Type>. The difference between ObservableCollection<> and other collections is that it notifies the UI when it’s properties change.
We bind the property TabControl.Items to the collection, and after that we cannot change property Items directly.
In Loaded event handler of main window, bind the property TabControl.Items to the collection:
void MyBrowser_Loaded(object sender, RoutedEventArgs e){myTabs.Items.Clear();myTabs.ItemsSource = webTabs;}Remember to attach the event handler in the constructor of main window.
public MyBrowser()
{InitializeComponent();KeyDown += new KeyEventHandler(MyBrowser_KeyDown);
Loaded += new RoutedEventHandler(MyBrowser_Loaded);
}I attach KeyDown event handler to make some hotkey for creating new tab (Ctrl+T) and closing current tab (Ctrl+W or Ctrl+F4)
void MyBrowser_KeyDown(object sender, KeyEventArgs e){if ((e.Key == Key.F4 || e.Key == Key.W) && (e.KeyboardDevice.Modifiers == ModifierKeys.Control))
{WebTab wt = myTabs.SelectedItem as WebTab;
if (wt != null)webTabs.Remove(wt);}if (e.Key == Key.T && e.KeyboardDevice.Modifiers == ModifierKeys.Control)
{WebTab wt = new WebTab();
wt.myParent = webTabs;webTabs.Add(wt);myTabs.SelectedItem = wt;}}In oder to make it looks like a browser, I declare a textbox and a button in main window to insert the address and navigate web pages.
Click event handler for the button is
private void btnGO_Click(object sender, RoutedEventArgs e){WebTab wt = myTabs.SelectedItem as WebTab;
if (wt == null){wt = new WebTab();
webTabs.Add(wt);myTabs.SelectedItem = wt;}try
{Uri mUri = new Uri(tbxAddress.Text);
wt.myFrame.Source = mUri;}catch (System.Exception ex)
{wt.myFrame.Content = ex.Message;}}Results
This is not a good design. Just for fun!! :)
For your helping, I also used this trick in my assignment ;)). Thanks again :D. About assigning shorcut keys, how about using Command :)
In bigger program, when catching event is complex, instead of catching KeyDown event in main window, we should use the Command program pattern to design in a better way. Hope someday I can see a blog post about Command in your blog ;)